package com.study.demo.app;


import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.net.Uri;
import android.net.http.SslError;
import android.os.Build;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewParent;
import android.webkit.DownloadListener;
import android.webkit.SslErrorHandler;
import android.webkit.ValueCallback;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import androidx.annotation.RequiresApi;

import java.util.Map;

/**
 * Created by xud on 2018/9/1.
 */
public class DWebView extends WebView {


    private static final String TAG = "DWebView";

    public static final String CONTENT_SCHEME = "file:///android_asset/";

    private ActionMode.Callback mCustomCallback;

    protected Context context;

    boolean isReady;

    private Map<String, String> mHeaders;


    public DWebView(Context context) {
        super(context);
        init(context);
    }

    public DWebView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public DWebView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public DWebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context);
    }


    public void setHeaders(Map<String, String> mHeaders) {
        this.mHeaders = mHeaders;
    }


    protected void init(Context context) {
        this.context = context;
        WebDefaultSettingManager.getInstance().toSetting(this);

        setWebViewClient(new DWebViewClient());

        setDownloadListener(downloadListener);
        setBackgroundColor(0);
        MyWebChromeClient myWebChromeClient = new MyWebChromeClient();
        setWebChromeClient(myWebChromeClient);
        myWebChromeClient.setBnWebFileChoseListener(new WebFileChoseListener() {
            @Override
            public void getFile(ValueCallback valueCallback) {
                if (null != webFileChoseListener) {
                    webFileChoseListener.getFile(valueCallback);
                }
            }
        });

    }

    @Override
    public ActionMode startActionMode(ActionMode.Callback callback) {
        final ViewParent parent = getParent();
        if (parent != null) {
            return parent.startActionModeForChild(this, wrapCallback(callback));
        }
        return null;
    }

    @TargetApi(Build.VERSION_CODES.M)
    @Override
    public ActionMode startActionMode(ActionMode.Callback callback, int type) {
        final ViewParent parent = getParent();
        if (parent != null) {
            return parent.startActionModeForChild(this, wrapCallback(callback), type);
        }
        return null;
    }

    private ActionMode.Callback wrapCallback(ActionMode.Callback callback) {
        if (mCustomCallback != null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                return new CallbackWrapperM(mCustomCallback, callback);
            } else {
                return new CallbackWrapperBase(mCustomCallback, callback);
            }
        }
        return callback;
    }

    public void setCustomActionCallback(ActionMode.Callback callback) {
        mCustomCallback = callback;
    }

    private static class CallbackWrapperBase implements ActionMode.Callback {
        private final ActionMode.Callback mWrappedCustomCallback;
        private final ActionMode.Callback mWrappedSystemCallback;

        public CallbackWrapperBase(ActionMode.Callback customCallback, ActionMode.Callback systemCallback) {
            mWrappedCustomCallback = customCallback;
            mWrappedSystemCallback = systemCallback;
        }

        @Override
        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
            return mWrappedCustomCallback.onCreateActionMode(mode, menu);
        }

        @Override
        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
            return mWrappedCustomCallback.onPrepareActionMode(mode, menu);
        }

        @Override
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
            return mWrappedCustomCallback.onActionItemClicked(mode, item);
        }

        @Override
        public void onDestroyActionMode(ActionMode mode) {
            try {
                mWrappedCustomCallback.onDestroyActionMode(mode);
                mWrappedSystemCallback.onDestroyActionMode(mode);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    @TargetApi(Build.VERSION_CODES.M)
    private static class CallbackWrapperM extends ActionMode.Callback2 {
        private final ActionMode.Callback mWrappedCustomCallback;
        private final ActionMode.Callback mWrappedSystemCallback;

        public CallbackWrapperM(ActionMode.Callback customCallback, ActionMode.Callback systemCallback) {
            mWrappedCustomCallback = customCallback;
            mWrappedSystemCallback = systemCallback;
        }

        @Override
        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
            return mWrappedCustomCallback.onCreateActionMode(mode, menu);
        }

        @Override
        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
            return mWrappedCustomCallback.onPrepareActionMode(mode, menu);
        }

        @Override
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
            return mWrappedCustomCallback.onActionItemClicked(mode, item);
        }

        @Override
        public void onDestroyActionMode(ActionMode mode) {
            mWrappedCustomCallback.onDestroyActionMode(mode);
            mWrappedSystemCallback.onDestroyActionMode(mode);
        }

        @Override
        public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
            if (mWrappedCustomCallback instanceof ActionMode.Callback2) {
                ((ActionMode.Callback2) mWrappedCustomCallback).onGetContentRect(mode, view, outRect);
            } else if (mWrappedSystemCallback instanceof ActionMode.Callback2) {
                ((ActionMode.Callback2) mWrappedSystemCallback).onGetContentRect(mode, view, outRect);
            } else {
                super.onGetContentRect(mode, view, outRect);
            }
        }
    }

    public void setContent(String htmlContent) {
        try {
            loadDataWithBaseURL(CONTENT_SCHEME, htmlContent, "text/html", "UTF-8", null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    private void load(String trigger) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            evaluateJavascript(trigger, null);
        } else {
            loadUrl(trigger);
        }
    }

    @Override
    public void loadUrl(String url) {
        super.loadUrl(url);
        Log.e(TAG, "DWebView load url: " + url);
        resetAllStateInternal(url);
    }

    @Override
    public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {
        super.loadUrl(url, additionalHttpHeaders);
        Log.e(TAG, "DWebView load url: " + url);
        resetAllStateInternal(url);
    }


    @Override
    public void goBack() {
        super.goBack();
    }

    private boolean mTouchByUser;

    public boolean isTouchByUser() {
        return mTouchByUser;
    }

    private void resetAllStateInternal(String url) {
        if (!TextUtils.isEmpty(url) && url.startsWith("javascript:")) {
            return;
        }
        resetAllState();
    }

    // 加载url时重置touch状态
    protected void resetAllState() {
        mTouchByUser = false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mTouchByUser = true;
                break;
        }
        return super.onTouchEvent(event);
    }

    public class DWebViewClient extends WebViewClient {

        public static final String SCHEME_SMS = "sms:";

        /**
         * url重定向会执行此方法以及点击页面某些链接也会执行此方法
         *
         * @return true:表示当前url已经加载完成，即使url还会重定向都不会再进行加载 false 表示此url默认由系统处理，该重定向还是重定向，直到加载完成
         */
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            Log.e(TAG, "shouldOverrideUrlLoading 11 url: " + url);
            // 当前链接的重定向, 通过是否发生过点击行为来判断
            if (!isTouchByUser()) {
                return super.shouldOverrideUrlLoading(view, url);
            }
            // 如果链接跟当前链接一样，表示刷新
            if (getUrl().equals(url)) {
                return super.shouldOverrideUrlLoading(view, url);
            }
            if (url.startsWith("http:") || url.startsWith("https:")) {
                // 控制页面中点开新的链接在当前webView中打开
                view.loadUrl(url, mHeaders);
                return false;
            } else {
                try {
                    Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
                    context.startActivity(intent);
                } catch (Exception e) {//防止crash (如果手机上没有安装处理某个scheme开头的url的APP, 会导致crash)
                    e.printStackTrace();
                    return true;//没有安装该app时，返回true，表示拦截自定义链接，但不跳转，避免弹出上面的错误页面
                }
                return true;
            }
        }

        @RequiresApi(api = Build.VERSION_CODES.N)
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
            Log.e(TAG, "shouldOverrideUrlLoading 22 url: " + request.getUrl());

            return shouldOverrideUrlLoading(view, request.getUrl().toString());
        }


        @Override
        public void onPageFinished(WebView view, String url) {
            Log.e(TAG, "onPageFinished url:" + url);
            if (!TextUtils.isEmpty(url) && url.startsWith(CONTENT_SCHEME)) {
                isReady = true;
            }

        }

        @Override
        public void onPageStarted(WebView view, String url, Bitmap favicon) {
            Log.e(TAG, "onPageStarted url: " + url);

        }

        @Override
        public void onScaleChanged(WebView view, float oldScale, float newScale) {
            super.onScaleChanged(view, oldScale, newScale);
        }

        @TargetApi(21)
        @Override
        public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
            return shouldInterceptRequest(view, request.getUrl().toString());
        }

        @Override
        public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
            return null;
        }

        @Override
        public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
            super.onReceivedError(view, errorCode, description, failingUrl);
            if (errorListener != null) {
                errorListener.onReceivedError(view, errorCode, description, failingUrl);
            }
            Log.e(TAG, "webview error" + errorCode + " + " + description + " failingUrl :" + failingUrl);
        }

        @Override
        public void onReceivedSslError(WebView view, final SslErrorHandler handler, SslError error) {
            String channel = "";
            if (!TextUtils.isEmpty(channel) && channel.equals("play.google.com")) {
                final AlertDialog.Builder builder = new AlertDialog.Builder(context);
                String message = "SSL证书错误。";
                switch (error.getPrimaryError()) {
                    case SslError.SSL_UNTRUSTED:
                        message = "SSL证书颁发机构未受信任。";
                        break;
                    case SslError.SSL_EXPIRED:
                        message = "SSL证书已过期。";
                        break;
                    case SslError.SSL_IDMISMATCH:
                        message = "SSL安全证书域名与网站网址不一致。";
                        break;
                    case SslError.SSL_NOTYETVALID:
                        message = "SSL证书还未生效。";
                        break;
                }
                message += "是否继续打开该网页?";

                builder.setTitle("SSL证书错误。");
                builder.setMessage(message);
                builder.setPositiveButton("继续", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        handler.proceed();
                    }
                });
                builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        handler.cancel();
                    }
                });
                final AlertDialog dialog = builder.create();
                dialog.show();
            } else {
                handler.proceed();
            }
        }
    }


    private DownloadListener downloadListener = new DownloadListener() {
        @Override
        public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {

            try {
                Uri uri = Uri.parse(getUrl());
                Intent intent = new Intent(Intent.ACTION_VIEW, uri);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                context.startActivity(intent);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    };

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
    }

    public boolean isReady() {
        return isReady;
    }

    private OnReceivedErrorListener errorListener;

    public void setErrorListener(OnReceivedErrorListener errorListener) {
        this.errorListener = errorListener;
    }

    private WebFileChoseListener webFileChoseListener;

    public void setBnWebFileChoseListener(WebFileChoseListener webFileChoseListener) {
        this.webFileChoseListener = webFileChoseListener;
    }

    private IActionFromJsParam jsParam;

    public void setJsParam(IActionFromJsParam jsParam) {
        this.jsParam = jsParam;
    }
}
